/* The file is saved in UTF-8 codepage.
 * Check: «Stereotype», Section mark-§, Copyright-©, Alpha-α, Beta-β, Smile-☺
 */
package cz.pecinovsky.english.lootp._134_;


import cz.pecinovsky.english.lootp.manager.Controller;


import cz.pecinovsky.english.lootp.manager.CanvasManager_Envelope.               CanvasManager;


//import cz.pecinovsky.english.lootp.utility.IO;


import cz.pecinovsky.english.lootp._134_.Car_134_.Car;
import cz.pecinovsky.english.lootp._134_.Ring_134_.Ring;
import cz.pecinovsky.english.lootp._134_.IRace_138_.IRace;
import cz.pecinovsky.english.lootp._134_.IRacer_137_.IRacer;
import cz.pecinovsky.english.lootp._134_.RoadField_134_.RoadField;
import cz.pecinovsky.english.lootp._134_.Arrow_134_.Arrow;
import cz.pecinovsky.english.lootp._134_.Vozidlo_B_137_.Vozidlo_B;


import cz.pecinovsky.english.lootp._134_.Závod_L_138_      .Závod_L;
////////////////////////////////////////////////////////////////////////////////
//%%+  +++++ End of ignored starting test - place for imports +++++
//%I+
//import cz.pecinovsky.english.lootp.canvasmanager.IControlled;
//import cz.pecinovsky.english.lootp.canvasmanager.Controller;
//import cz.pecinovsky.english.lootp.canvasmanager.CanvasManager;
//%I-

import cz.pecinovsky.english.lootp.utility.NamedColor;
import cz.pecinovsky.english.lootp.utility.IO;
import cz.pecinovsky.english.lootp.utility.Position;

//%A+ >139
import java.util.Collection;
//%A-
import java.util.HashMap;
import java.util.Map;
//%X+ xxxxx Start of the ovejumped text with declaration of the envelope xxxxx
/*******************************************************************************
 *<pre>
 * Předchozí:   Není - toto_je_nově_definovaná_třída
 *              Ttt v projektu Ppp
 * Následující: Prozatím není
 *
 * Projekt  Ppp
 *   + Přidáno
 *   - Odebráno
 *   ~ Změněno
 *</pre>
 */
public class Závod_L_138_
{    private Závod_L_138_() {}   static
////////////////////////////////////////////////////////////////////////////////
//%X- ----- End of the ovejumped text with declaration of the envelope -----


/*******************************************************************************
 * Instance třídy {@code Závod_L} představují závody, které je možno absolvovat.
 * Všechny závody se ale jezdí na okruhu vytvářeném metodou
 * {@link Ring#elko(Position, NamedColor)}.
 * K závodu se může přihlásit několik závodníků, kteří jej pak pojedou
 * všichni současně každý na své instanci okruhu.
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public class Závod_L implements IRace
{
//== CONSTANT CLASS ATTRIBUTES =================================================

    /** Canvas, na které se bude instance kreslit. */
    private static final CanvasManager CM = CanvasManager.getInstance();

//Následující tři konstanty závisejí na okruhu, na němž se bude jezdit.
//Tato verze předpokládá, že se bude provozovat okruh ELKO.

    /** Políčková šířka okruhu zvětšená o jeden sloupec. Je třeba ji znát proto,
     *  aby bylo možné umisťovat jednotlivé okruhy vedle sebe. */
    private static final int ŠÍŘKA_OKRUHU = 5;

    /** Políčková výška okruhu, aby bylo možno objednat výšku plátna. */
    private static final int VÝŠKA_OKRUHU = 4;



//== VARIABLE CLASS ATTRIBUTES =================================================
//== STATIC CONSTRUCTOR (CLASS INITIALIZER, STATIC INICITALIZING BLOCK) ========
//== CONSTANT INSTANCE ATTRIBUTES ==============================================

    /** Mapa přiřazující jednotlivým závodníkům informace,
     * které si o nich instance závodu pamatuje. */
    Map<IRacer, Info> závodníci = new HashMap<IRacer, Info>();

    /** Maximální přípustný maxZávodníků závodníků. */
    private final int maxZávodníků;

    /** Aktuálně nastavený module plátna. */
    private final int module;
//%A+ >139

    /** Počet kol, na která se závod jede. */
    private final int početKol;
//%A-



//== VARIABLE INSTANCE ATTRIBUTES ==============================================

    /** Systémový čas v okamžiku startu závodu. */
    private long čas0;

    /** Počet doposud registrovaných závodníků a tím i vytvořených okruhů. */
    private int poslední = 0;



//== CLASS GETTERS AND SETTERS =================================================
//== OTHER NON-PRIVATE CLASS METHODS ===========================================

//##############################################################################
//== CONSTUCTORS AND FACTORY METHODS ===========================================

    /***************************************************************************
     * Vytvoří instanci schopnou pořádat závody na okruzích
     * vrácených metodou {@link Ring.elko(Position,NamedColor)}.
     *
     * @param maxZávodníků Maximální maxZávodníků současně závodícíh závodníků
     * @param module        Požadovaná délka strany jednoho pole plátna
     */
    public Závod_L(int maxZávodníků, int module)
    {
        this(maxZávodníků, module, 1);
    }


    /***************************************************************************
     * Vytvoří instanci schopnou pořádat závody na okruzích
     * vrácených metodou {@link Ring.elko(Position,NamedColor)}.
     *
     * @param maxZávodníků Maximální maxZávodníků současně závodícíh závodníků
     * @param module        Požadovaná délka strany jednoho pole plátna
//%A+ >139
     * @param početKol      Počet kol, na která se závod jede
//%A-
     */
    public Závod_L(int maxZávodníků, int module, int početKol)
    {
        this.maxZávodníků = maxZávodníků;
        this.module        = module;
//%A+ >139
        this.početKol     = početKol;
//%A-
        CM.setStepAndSize(module, maxZávodníků*ŠÍŘKA_OKRUHU - 1, VÝŠKA_OKRUHU);
    }



//== ABSTRACT METHODS ==========================================================
//== INSTANCE GETTERS AND SETTERS ==============================================
//== OTHER NON-PRIVATE INSTANCE METHODS ========================================

    /***************************************************************************
     * Otevře dialogové okno, v němž závodníkům oznámí, že je odstartováno,
     * a poté, co je zavřou, změří systémový čas.
     */
    public void start()
    {
        IO.inform("Po potvrzení tohoto okna se začne měřit čas");
//%A+ >139
        Collection<Info> infa = závodníci.values();
        for (Info info : infa) {
            info.řadič.start();
        }
//%A-
        čas0 = System.currentTimeMillis();
    }


    /***************************************************************************
     * Ukončí závod, čím zruší všechny registerFor a připraví se na nové.
     */
    public void stop()
    {
        závodníci.clear();
    }


    /***************************************************************************
     * Zaregistruje zadaného závodníka, umístí jej na start závodu,
     * zjistí si od uživatele potřebné klávesy
     * a přihlásí závodníka jako posluchače klávesnice.
     *
     * @param závodník Závodník registrující se pro daný závod
     */
    @Override
    public void registration(IRacer závodník)
    {
        if (poslední >= maxZávodníků) {
            IO.inform("Je možno zaregistrovat pouze: " + maxZávodníků +
                      " závodníků");
            return;
        }
        if (závodníci.containsKey(závodník)) {
            IO.inform("Závodníka nelze zaregistrovat dvakrát: " + závodník);
            return;
        }
        Ring     okruh  = připravDalšíOkruh(); //Opraví i atribut poslední
        RoadField start  = okruh.getPočátek();
        Position    pozice = start.getPosition();
        Controller     řadič  = Controller.vytvořPro(závodník);
        RoadField cíl    = start.getDalší();

//%I+ <139
//        Info info = new Info(okruh, start, cíl, řadič);
//%I-
//%A+ >139
        Info info = new Info(okruh, start, cíl, řadič, početKol);
//%A-
        závodníci.put(závodník, info);

        závodník.setPosition(pozice);
        závodník.setModule(module);
        závodník.setDirection(start.getSměr());
//%I+ <139
//        řadič.start();
//%I-
    }


    /***************************************************************************
     * Prověří, že závodník dojel do správné průběžné cílové pozice.
     * Pokud dojel, připraví další průběžnou pozici, pokud nedojel, čeká dál.
     *
     * @param závodník Závodník hlásící změnu své pozice
     */
    @Override
    public void checkpoint(IRacer závodník)
    {
        Info info = závodníci.get(závodník);
        if (info == null) {
            return;                      //==========>
        }
        Position pz = závodník.getPosition();
        Position pc = info.cíl.getPosition();
        if (pz.equals(pc)) {
            if (info.cíl == info.start) {
//%I+ <139
//                konecZávodu(závodník, info);
//                return;                  //==========>
//%I-
//%A+ >139
                info.zbýváKol--;
                if (info.zbýváKol > 0) {
                    konecZávodu(závodník, info);
                    return;                  //==========>
                }
//%A-
            }
            info.cíl = info.cíl.getDalší();
        }
    }


    /***************************************************************************
     * Ukončí závod pro zadaného závodníka.
     *
     * @param závodník Závodník ukončivší závod
     */
    private void konecZávodu(IRacer závodník, Info info)
    {
        long nyní = System.currentTimeMillis();
        info.řadič.konec();
        int čas = (int)(nyní - čas0 + 50) / 100;
        System.out.println("Závodík " + závodník.getName() +
               " absolvoval okruh v čase " + čas/10 + "," + čas%10 + " sekund");
        závodníci.remove(závodník);
        if (závodníci.isEmpty()) {
            System.out.println("Konec závodu");
//            IO.inform("Konec závodu" +
//                  "\n\nvýsledky najdete ve standardním výstupu");
        }
    }


    /***************************************************************************
     * Vrátí řetězec sloužící jako textový "podpis" instance
     * používaný především při ladění.
     */
    @Override
    public String toString()
    {
        return "Závod_L(ELKO, počet=" + maxZávodníků + ", module=" + module + ")";
    }



//== PRIVATE AND AUXILIARY CLASS METHODS =======================================
//== PRIVATE AND AUXILIARY INSTANCE METHODS ====================================

    /***************************************************************************
     * Připraví další okruh a vrátí odkaz na něj.
     *
     * @return Odkaz na připravený okruh
     */
    private Ring připravDalšíOkruh()
    {
        int x = poslední * ŠÍŘKA_OKRUHU * module;
        Position pozice = new Position(x, 0);
        Ring okruh = Ring.elko(pozice, NamedColor.getNamedColor(poslední));
        poslední = poslední + 1;
        return okruh;
    }



//== EMBEDDED TYPES AND INNER CLASSES ==========================================

    /***************************************************************************
     * Interní přepravka obsahující základní informace o závodníkovi
     * a průběžném stavu jeho závodu.
     */
    private static class Info
    {
        /** Ring, na němž je závoděno. */
        private final Ring okruh;

        /** Pole, z nějž závod odstartuje. */
        private final RoadField start;

        /** Příští pole, na které má závodník dojet
         *  a na němž se bude kontrolovat, zda je skutečně projel.  */
        private RoadField cíl;

        /** Controller, zprostředkovávající ovládání závodníka z klávesnice. */
        private final Controller řadič;
//%A+ >139

        /** Kontrolovaný závodník. */
        private int zbýváKol;
//%A-


        /***********************************************************************
         * Definuje novou přepravku a inicializuje její atributy.
         */
//%I+ <139
//        Info(Ring okruh, RoadField start, RoadField cíl, Controller řadič)
//%I-
//%A+ >139
        Info(Ring okruh, RoadField start, RoadField cíl, Controller řadič, int kol)
//%A-
        {
            this.okruh    = okruh;
            this.start    = start;
            this.cíl      = cíl;
            this.řadič    = řadič;
//%A+ >139
            this.zbýváKol = kol;
//%A-
        }
    }



//== TESTING CLASSES AND METHODS ===============================================

    /***************************************************************************
     * Test method.
     */
    public static void test()
    {
        Závod_L závod = new Závod_L(3, 100);

        Arrow  arrow = new Arrow(0, 0, NamedColor.WHITE);
        Vozidlo_B v1 = new Vozidlo_B(arrow);
        v1.setName("Jednička");
        v1.registerFor(závod);

        Car  car = new Car(0, 0, NamedColor.WHITE);
        Vozidlo_B v2 = new Vozidlo_B(car);
        v2.setName("Dvojka");
        v2.registerFor(závod);

        závod.start();
    }
//    /** @param args Parametry příkazového řádku - nepoužívané. */
//    public static void main(String[] args)  {  test();  }
    }

//%%- ---- Start of the ovejumped text with closing of the envelope ----
////////////////////////////////////////////////////////////////////////////////
//== TESTING CLASSES AND METHODS ===============================================

    /***************************************************************************
     * Test method.
     */
    public static void test()
    {
        Závod_L.test();

//        IO.inform("Až se pokocháš, stiskni OK");
//        System.exit(0);
    }
    /** @param args Parametry příkazového řádku - nepoužívané. */
    public static void main(String[] args)  {  test();  }
}
